(function(){
	function setSelectText(editor, text)
	{
		if (!editor) return;
		editor.focus();
		if (editor.document && editor.document.selection)
			editor.document.selection.createRange().text = text;
		else if ("selectionStart" in editor)
		{
			var str = editor.value, start = editor.selectionStart;
			editor.value = str.substr(0, start) + text +
	 			str.substring(editor.selectionEnd, str.length);
			editor.selectionStart = start + text.length;
			editor.selectionEnd = start + text.length;
		}
	}
	
	function getSelectText(editor)
	{
		if (!editor) return;
		editor.focus();
		if (editor.document && editor.document.selection)
			return editor.document.selection.createRange().text;
		else if ("selectionStart" in editor)
			return editor.value.substring(editor.selectionStart, editor.selectionEnd);
	}
	
    var base64_encode_dict = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", publicKey = "6Ieq55Sx5piv5LiA56eN5Lyg6K+0";
    var crc32Table = new Array(256);
    (function(){
        var i, j, k;
        for (i = 0; i < 256; i++) {
            k = i;
            for (j = 0; j < 8; j++) 
                if (k & 1) 
                    k = (k >> 1) ^ 0xedb88320;
                else 
                    k >>= 1;
            crc32Table[i] = k;
        }
    })();
    
    function crc32(str){
        if (!str) 
            return;
        var crc = 0xffffffff, i, code;
        for (i = 0; i < str.length; i++) {
            code = str.charCodeAt(i);
            if (code > 0xff) {
                crc = (crc >> 8) ^ crc32Table[(crc & 0xff) ^ (code & 0xff)];
                crc = (crc >> 8) ^ crc32Table[(crc & 0xff) ^ (code >> 8)];
            }
            else 
                crc = (crc >> 8) ^ crc32Table[(crc & 0xff) ^ code];
        }
        return crc ^ 0xffffffff;
    }
    
    function xor(str, key){
        if (!key || !str) 
            return str;
        var result = [], i, j = 0;
        for (i = 0; i < str.length; i++) {
            result.push(String.fromCharCode(str.charCodeAt(i) ^ key.charCodeAt(j)));
            j = (j + 1) % key.length;
        }
        return result.join("");
    }
    
    /**
     * 格式化字符串
     * @param {String} template 字符模板 ${属性名}
     * @param {Object} json 对象 数组用$(0)替换元素
     */
    function format(template, json){
        if (!json) 
            return template;
        return template &&
        template.replace(/\$\{(.+?)\}/g, function(){
            return json[arguments[1]];
        })
    }
    function encodeBase64(str){
        var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, enc = [];
        str = encodeUTF8(str);
        do {
            o1 = str.charCodeAt(i++);
            o2 = str.charCodeAt(i++);
            o3 = str.charCodeAt(i++);
            
            bits = o1 << 16 | o2 << 8 | o3;
            
            h1 = bits >> 18 & 0x3f;
            h2 = bits >> 12 & 0x3f;
            h3 = bits >> 6 & 0x3f;
            h4 = bits & 0x3f;
            
            if (isNaN(o3)) 
                h4 = 64;
            if (isNaN(o2)) 
                h3 = 64;
            
            enc.push(base64_encode_dict.charAt(h1) + base64_encode_dict.charAt(h2) + base64_encode_dict.charAt(h3) + base64_encode_dict.charAt(h4));
        }
        while (i < str.length);
        
        return enc.join("");
    }
    
    function decodeBase64(str){
        var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, enc = [];
        
        do {
            h1 = base64_encode_dict.indexOf(str.charAt(i++));
            h2 = base64_encode_dict.indexOf(str.charAt(i++));
            h3 = base64_encode_dict.indexOf(str.charAt(i++));
            h4 = base64_encode_dict.indexOf(str.charAt(i++));
            
            bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
            
            o1 = bits >> 16 & 0xff;
            o2 = bits >> 8 & 0xff;
            o3 = bits & 0xff;
            
            if (h3 == 64) 
                enc.push(String.fromCharCode(o1));
            else 
                if (h4 == 64) 
                    enc.push(String.fromCharCode(o1, o2));
                else 
                    enc.push(String.fromCharCode(o1, o2, o3));
        }
        while (i < str.length);
        return decodeUTF8(enc.join(""));
    }
    
    function encodeUTF8(str){
        str = str.replace(/[\u0080-\u07ff]/g, function(c){
            var cc = c.charCodeAt(0);
            return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
        }).replace(/[\u0800-\uffff]/g, function(c){
            var cc = c.charCodeAt(0);
            return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f);
        });
        return str;
    }
    
    function decodeUTF8(str){
        str = str.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, function(c){
            var cc = (c.charCodeAt(0) & 0x1f) << 6 | (c.charCodeAt(1) & 0x3f);
            return String.fromCharCode(cc);
        }).replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, function(c){
            var cc = (c.charCodeAt(0) & 0x0f) << 12 | (c.charCodeAt(1) & 0x3f) << 6 | (c.charCodeAt(2) & 0x3f);
            return String.fromCharCode(cc);
        });
        return str;
    }
    
    function scaner(node, encode){
        var m, p, t, d = node.document || node.ownerDocument, c, $ = /#.*\*\*/.test(location);
        encode = encode || /(div)/i.test(node.tagName) && d.activeElement == node;
        if (/^i?frame$/i.test(node.tagName)) { // 嵌套扫描 code
            try {
                scaner(node.contentWindow.document, /^(on)$/i.test(node.contentWindow.document.designMode));
            } 
            catch (e) {
            }
            return;
        }
        if (/^(textarea|input)$/i.test(node.tagName)) { // 输入框，采用编码 code
            if (node.readonly || /input/i.test(node.tagName) && node.type != "text") 
                return; // 只读 或者非输入框 code
            if (d && d.activeElement != node) 
                return; // 非聚焦输入框 code
            var selText = getSelectText(node), value = selText || node.value;
			if (!value) return; // 无内容
            if (value.match(/\[?#([\/a-zA-Z\d+=]+)#\]?/) || value.match(/\*(-?\d+),(-?\d+),([\/a-zA-Z\d+=]+)\*/)) 
                return; // 已经存在编码 code
            value = value.replace(/^.+$/mg, function(all){
                if (!$) 
                    return format("[#${0}#]", [encodeBase64(all)]);
                var key = parseInt(Math.random() * 10000);
                return format("*${0},${1},${2}*", [key, crc32(all), encodeBase64(xor(xor(all, String(key * Math.PI), publicKey)))]);
            });
			if (selText) { // 存在选择选
				setSelectText(node, value);
			} else {
				node.value = value;
			}
            return;
        }
        if (node.nodeType == 3 && node.nodeValue) { // 文本节点 code
            if (encode) { // 编码方式 code
                m = node.nodeValue.match(/\[?#([\/a-zA-Z\d+=]+)#\]?/);
                if (m) 
                    return; // 已经存在编码 code
                m = node.nodeValue.match(/\*(-?\d+),(-?\d+),([\/a-zA-Z\d+=]+)\*/);
                if (m) 
                    return; // 已经存在编码 新格式 code
                node.nodeValue = node.nodeValue.replace(/^.+$/mg, function(all){
                    if (!$) 
                        return format("[#${0}#]", [encodeBase64(all)]);
                    var key = parseInt(Math.random() * 10000);
                    return format("*${0},${1},${2}*", [key, crc32(all), encodeBase64(xor(xor(all, String(key * Math.PI), publicKey)))]);
                });
            }
            else { // 解码方式 code
                t = node.nodeValue.replace(/\[?#([\/a-zA-Z\d+=]+)#\]?|\*(\d+),(-?\d+),([\/a-zA-Z\d+=]+)\*/g, function(all){
                    m = all.match(/\[?#([\/a-zA-Z\d+=]+)#\]?/);
                    if (m) {
                        c = true;
                        return decodeBase64(m[1]); // 其他元素采用解码 code
                    }
                    m = all.match(/\*(-?\d+),(-?\d+),([\/a-zA-Z\d+=]+)\*/);
                    if (m) {
                        var key = +m[1], s = xor(xor(decodeBase64(m[3]), String(key * Math.PI), publicKey));
                        if (crc32(s) != +m[2]) 
                            return all;
                        c = true;
                        return s;
                    }
                });
                if (c) 
                    node.nodeValue = t;
            }
            return;
        }
        var nodes = node.childNodes, i = nodes.length;
        while (i--) {
            scaner(nodes[i], encode);
        }
    }
    if (/^(community|bbs)\.csdn\.net$/.test(document.domain)) { // 处理导航页面 code
        document.domain = "csdn.net";
    }
    scaner(document.body);
})();
